/**************************************************************************************
*  j_vm.c
*
*  This file contains main bytecode execution loop as well as various support
*  functions.
*
**************************************************************************************
*
*  The contents of this file are subject to the RTJ COMPUTING non-commercial
*  source code license agreement ("License"); you may not use this file except in
*  compliance with the License. A copy of the License (license.terms) can be
*  found in the root of installation folder or on the web at
*  http://www.rtjcom.com/Files/license.terms
*
*  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
*  OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
*  MERCHANTABILITY,  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
*  IN NO EVENT SHALL RTJ COMPUTING BE LIABLE FOR ANY CLAIM, DAMAGES
*  OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
*  ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
*  OTHER DEALINGS IN THE SOFTWARE.
*
*  Copyright (c) 2000-2002 RTJ Computing Pty. Ltd. All rights reserved.
*
**************************************************************************************/

#include "jvm.h"
#include "jcommon.h"

#define RETURN_FAIL   0
#define METH_RETURN   1
#define RUN_RETURN    2

static int16 getExceptionIndex(int16 *detail);
static bool createExceptions(void);
static bool execAll_clinit(void);
static bool check_clinit(bitem_t class_ptr);
static void mark_clinit(bitem_t class_ptr);
static bool verifyApp(void);
static void usrFunc(void (*f)(uint16, char *), uint16 code);
static void setField(ref_t *ref, field_t *field, uint32 value);
static bool handle_native(void);
static int16 handle_return(frame_t *init_frame);
static bool handle_new(bitem_t class_ptr);
static bool handle_throw(ref_t *excep);
static uint8 *findExcepEntry(ref_t *excep_ref);
static bool addToStrTable(uint16 hash, ref_t *string);
static jint getStringCount(int32 param);
static jchar *getStringArray(int32 param);
static jint getArraySize(int32 param);
static void *getArray(int32 param);
static uint32 getSysTime(void);
static void updateEventCounter(uint16 index);


/****************************************************************************
* Initialises all variables and starts the virtual machine interpreter.
* Order of data on the application heap:
* 1. stack trace
* 2. static fields
* 3. strings table
* 4. threads table
* 5. references table
* 6. objects data
*
* Input:        config  pointer to configuration data
* Output:       none
* Return:       none
****************************************************************************/
void vmStart(vm_config_t *cfg)
{
    int16 excep, detail;
    uint8 *temp_ptr;
    bool retval;
    bitem_t main_meth;


    /* setup pointers in configuration structure */
    config = cfg;
    config->vmGetStringCount = getStringCount;
    config->vmGetStringArray = getStringArray;
    config->vmGetArraySize = getArraySize;
    config->vmGetArray = getArray;
    config->vmGetSysTime = getSysTime;
    config->vmUpdateEventCounter = updateEventCounter;

    usrFunc(config->usrNotify, NOTIFY_VM_INIT);

    /* Verify that the application is present */
    if (!verifyApp())
    {
        usrFunc(config->usrNotify, NOTIFY_VM_NOAPP);
        return;
    }

    /* Allocate memory on the heap for class locks */
    heap_ptr = config->heap_start;
#if ENABLE_THREADS
    class_locks = (classlock_t *)heap_ptr;
    heap_ptr += (APP->class_count * sizeof(classlock_t));
#endif

    /* Allocate memory on the heap for software timers */
    sw_timers = NULL;
    if (config->timers > 0)
    {
        sw_timers = (timer_t *)heap_ptr;
        heap_ptr += (config->timers * sizeof(timer_t));
    }

    /* 1. Allocate memory on the heap for stack trace buffer */
    config->stack_trace = (trace_t *)heap_ptr;
    heap_ptr += STACKTRACE_SIZE;

    /* 2. Allocate memory on the heap for static fields */
    static_start = (value_t *)heap_ptr;
    heap_ptr += APP->static_size;

    /* 3. Allocatoe memory on the heap for strings table */
    strings = (strings_t *)heap_ptr;
    heap_ptr += config->strings * sizeof(strings_t);

    /* 4. Allocatoe memory on the heap for threads table */
    threads = (thread_t *)heap_ptr;
#if ENABLE_THREADS
    /* add one thread for executing main() method */
    config->threads++;
#else
    /* allow only one thread for the main() method */
    config->threads = 1;
#endif
    heap_ptr += config->threads * sizeof(thread_t);

    /* 5. Allocatoe memory on the heap for object references */
    refs = (ref_t *)heap_ptr;
    app_refs = refs;
    heap_ptr += config->references * sizeof(ref_t);

    /* 6. Thre rest of the heap is for objects data */
    objdata_start = heap_ptr;

    /* check if remaining heap is big enough */
    if (heap_ptr + MIN_OBJDATA_SIZE > config->heap_end)
    {
        usrFunc(config->usrNotify, NOTIFY_INSUF_HEAP);
        return;
    }
    clearMem(config->heap_start, (int32)(heap_ptr - config->heap_start));

    /* Initialise end of frames ptr., frame and instance sizes */
    frame_ptr = config->heap_end;
    frame_size = (uint16)(APP->frame_size + FRAME_HDR_SIZE);
    inst_size = (uint16)(APP->inst_size + sizeof(classhdr_t));

    vm_excep = 0;

    /* initialise threads scheduler -- part 1 */
    if (initThreads1())
    {
        /* Create all run-time exception objects */
        if (createExceptions())
        {
            /*
            ** Adjust start of references table. All references allocated so
            ** far are system ones and should not be used during GC.
            */
            app_refs = findFreeRef();
            if (app_refs != NULL)
            {
                /*
                ** Run class initialisation methods <clinit>.
                ** Before running <clinit> methods allocate space at the end of heap
                ** for table listing classes for which <clinit> has been run.
                ** When finished, the heap end is restored.
                */
                temp_ptr = config->heap_end;
                config->heap_end -= (APP->class_count * sizeof(clinit_list[0]));
                clinit_list = (bitem_t *)config->heap_end;
                frame_ptr = config->heap_end;

                clearMem(frame_ptr, temp_ptr - frame_ptr);
                retval = execAll_clinit();
                config->heap_end = temp_ptr;
                frame_ptr = temp_ptr;
                if (retval)
                {
                    /* run <init>() on thread with main() method */
                    if (initThreads2())
                    {
                        usrFunc(config->usrNotify, NOTIFY_VM_START);

                        /*
                        ** Initialise remaining params and start user applic. running
                        ** by invoking the main() method.
                        */
                        config->vmTimeSlice = vmTimeSlice;
                        vm_args_count = 0;
                        vm_status = 0;

                        main_meth = APP->main;
                        main_meth.ptr = BANK_ABS_LOC(main_meth.ptr);
                        run(main_meth);
                    }
                }
            }
            else
                vm_excep = MEMERR_NoRefs;
        }
    }

    /* execution should get here only if exception was generated */
    if (vm_excep == 0)
        vm_excep = INTERR_ProgEnd;

    excep = getExceptionIndex(&detail);
    usrFunc(config->usrNotify, (uint16)((excep << 8) | detail));
}


/****************************************************************************
* Subroutine will call FillInStackTrace to get list of methods on the
* current thread stack. Then, it will convert jvm_excep value into index to
* run-time exception table. For OutOfMemory and Internal error detail code
* is derived from jvm_excep.
*
* Input:        none
* Output:       detail  detail code for OutOfMemory and Internal errors
* Return:       index to exception table
****************************************************************************/
static int16 getExceptionIndex(int16 *detail)
{
    *detail = 0;
    if (vm_excep >= INTERR)
    {
        *detail = vm_excep;
        if (vm_excep >= MEMERR)
            return ERROR_OutOfMemory;

        return ERROR_Internal;
    }

    return vm_excep;
}


/****************************************************************************
* Subroutine clears the stack trace buffer and then it fills it with
* stack trace. Stack trace contains a list of excecuted methods and
* corresponding values of PC counters.
*
* Input:        none
* Output:       none
* Return:       none
****************************************************************************/
void fillInStackTrace(void)
{
    int16 i = 0;
    frame_t *frame;

    frame = thr_active->curr_frame;
    if (frame != NULL)
    {
        frame->pc = vm_pc;
        frame->sp = vm_sp;

        config->stack_trace->count = 0;
        while (frame != NULL && i < MAX_TRACES)
        {
            config->stack_trace->trace[i].method.ptr = BANK_REL_LOC(frame->method.ptr);
            config->stack_trace->trace[i].method.bank = frame->method.bank;
            config->stack_trace->trace[i].pc = BANK_REL_LOC(frame->pc);
            frame = frame->prev;
            config->stack_trace->count++;
            i++;
        }
    }
}


/****************************************************************************
* Creates all run-time exception objects. For each exception creates
* a String object and optionally initialised to appropriate excep. text.
* It then calls constructor <init>().
* for each exception object.
*
* Input:        none
* Output:       none
* Return:       true if successful, otherwise false is returned.
****************************************************************************/
static bool createExceptions(void)
{
    int16   i;
    excep_t *elist;
    ref_t   *ref;
#if ENABLE_STRING_EXCEPTIONS
    ref_t   *strRef;
#endif

    /*
    ** RUN_SPECIAL_FLAG is set so the run() will return when it
    ** encounters return bytecode.
    */
    vm_status |= RUN_SPECIAL_FLAG;

    elist = (excep_t *)APP_ABS_LOC(APP->excep_list);
    if (elist->count != RUNTIME_EXCEPTIONS)
    {
        vm_excep = INTERR_RuntimeExcepCount;
        return false;
    }

    for (i=0; i < RUNTIME_EXCEPTIONS; i++)
    {
        ref = createObject(elist->class_ptr[i]);
#if ENABLE_STRING_EXCEPTIONS
        strRef = createStringObject((const_t *)excep_str_table[i]);
        if (ref == NULL || strRef == NULL || run_init(ref, strRef) == false)
            return false;
#else
        if (ref == NULL || run_init(ref, NULL) == false)
            return false;
#endif
        excep_refs[i] = ref;
    }

    return true;
}


/****************************************************************************
* Executes <clinit> methods for all classes in the class table.
*
* Input:        none
* Output:       none
* Return:       true if successful, otherwise false is returned.
****************************************************************************/
static bool execAll_clinit(void)
{
    uint16  i;
    bitem_t class_ptr;

    /*
    ** RUN_SPECIAL_FLAG is set so the Run subroutine will
    ** return when it encounters return bytecode.
    */
    vm_status |= RUN_SPECIAL_FLAG;

    /* if no classes present -- just return */
    if (APP->class_count == 0)
        return true;

    for (i=0; i < (uint16)APP->class_count; i++)
    {
        class_ptr = APP->class_tbl[i];
        class_ptr.ptr = BANK_ABS_LOC(class_ptr.ptr);
        if (!run_clinit(class_ptr))
            return false;
    }

    return true;
}


/****************************************************************************
* Runs <clinit> method for specified class. If class is a subclass then
* the super class' <clinit> is run first by recursively calling itself.
*
* Input:        class_ptr   pointer to class structure
* Output:       none
* Return:       true if successful, otherwise false is returned.
****************************************************************************/
bool run_clinit(bitem_t class_ptr)
{
    bool    retval = true;
    bitem_t super_ptr;

    /* switch into class' bank */
    config->switchBank(class_ptr.bank);

    /* skip array type classes */
    if ((((class_t *)class_ptr.ptr)->flags & ACC_ARRAY_TYPE) == 0)
    {
        /* run superclass' <clinit> if exists */
        if (((class_t *)class_ptr.ptr)->super.ptr != NULL)
        {
            super_ptr = ((class_t *)class_ptr.ptr)->super;
            super_ptr.ptr = BANK_ABS_LOC(super_ptr.ptr);
            if (!run_clinit(super_ptr))
                retval = false;
        }

        config->switchBank(class_ptr.bank);
        if (retval && ((class_t *)class_ptr.ptr)->clinit != NULL && check_clinit(class_ptr))
        {
            vm_args_count = 0;
            mark_clinit(class_ptr);         /* mark this <clinit> as being executed */

            /* use class_ptr to pass clinit location to run() function */
            class_ptr.ptr = BANK_ABS_LOC(((class_t *)class_ptr.ptr)->clinit);
            if (!run(class_ptr))
                retval = false;
        }
    }
    config->switchBank(vm_curr_bank);
    return retval;
}


/****************************************************************************
* Checks if the specified <clinit> has been executed. It goes trough the
* list of executed <clinit> methods and checks if the specified address is
* listed or not.
*
* Input:        class_ptr   pointer to class structure
* Output:       none
* Return:       true if <clinit> has not been executed,
*               otherwise false is returned.
****************************************************************************/
static bool check_clinit(bitem_t class_ptr)
{
    uint16 i;

    for (i=0; i < (uint16)APP->class_count; i++)
    {
        if (clinit_list[i].ptr == ((class_t *)class_ptr.ptr)->clinit &&
            clinit_list[i].bank == class_ptr.bank)
            return false;
    }

    return true;
}


/****************************************************************************
* Includes specified <clinit> address in the list of executed <clinit>
* methods. It then updates clinit_count variable.
*
* Input:        class_ptr   location of class containing <clinit>
* Output:       none
* Return:       none
****************************************************************************/
static void mark_clinit(bitem_t class_ptr)
{
    uint16 i;

    for (i=0; i < (uint16)APP->class_count; i++)
    {
        if (clinit_list[i].ptr == NULL)
        {
            clinit_list[i].ptr = ((class_t *)class_ptr.ptr)->clinit;
            clinit_list[i].bank = class_ptr.bank;
            return;
        }
    }
}


/****************************************************************************
* Executes the method's bytecodes.
* First, it creates new method frame and initialises pc and sp counters. Then,
* it executes all bytecodes of the method.
* Global variable bc_action can take following values after executing
* the bytecode:
*       0               - no special action required
*       ACTION_RETURN   - method should finish its execution (return statement
*                         encountered)
*       ACTION_INVOKE   - invoke another java method
*       ACTION_NATIVE   - invoke native method
*       ACTION_NEW      - create instance of class
*       ACTION_THROW    - checked exception has been thrown
*
* If on return from method there are no other frames on the stack then the
* current thread is terminated. If  there are no other threads to run then
* EXCEP_ProgEnd is generated.
*
* If RUN_SPECIAL_FLAG is set in vm_status then this method will return after
* encountering the return bytecodes.
*
* Input:        method   pointer to method to execute
* Output:       none
* Return:       true if successful, otherwise false is returned.
****************************************************************************/
bool run(bitem_t method)
{
    int16    i, bc_action;
    bitem_t  exec_method = method;
    frame_t *init_frame = NULL;
    bc_handler handler;
    bool     go = true;

    /* method execution loop */
    while (true)
    {
        if (!createFrame(exec_method))
        {
            handle_excep();
            if (vm_excep1 != 0)
                vm_excep = vm_excep1;
            break;
        }

#if ENABLE_THREADS
        if ((((method_t *)exec_method.ptr)->flags & (ACC_SYNCH | ACC_STATIC)) == ACC_SYNCH)
        {
            /* acquire lock for object on which is method being executed */
            acquireObjectLock(thr_active, GET_REF(vm_args[0].val));
        }
        else if ((((method_t *)exec_method.ptr)->flags & (ACC_SYNCH | ACC_STATIC)) == (ACC_SYNCH | ACC_STATIC))
        {
            /* acquire class wide lock */
            acquireClassLock(thr_active);
        }
#endif
        /* remember method's frame so we know when to return from run() */
        if (init_frame == NULL)
          init_frame = thr_active->curr_frame;

        /* copy args (if present) to local variables */
        for (i=0; i < vm_args_count; i++)
            thr_active->curr_frame->locals[i] = vm_args[i];

        /* bytecodes execution loop */
        while (true)
        {
#if ENABLE_THREADS
            switchThreads();
#elif ENABLE_WDT
            config->clearWdt();
#endif
            bc_item.ptr = NULL;
            handler = bytecode_table[*vm_pc];
            vm_pc++;
            bc_action = handler();
            if (bc_action < 0)
            {
                if (!handle_excep())
                  return false;
            }

            switch (bc_action)
            {
                case 0:
                    /* no special handling required -- goto next bytecode */
                    continue;

                case ACTION_RETURN:
#if ENABLE_THREADS
                    /* release synchronization locks -- if any */
                    if (!releaseLocks())
                        return false;
#endif
                    i = handle_return(init_frame);
                    if (i == RUN_RETURN)
                        return true;
                    go = (bool)(i != RETURN_FAIL);
                    break;

                case ACTION_INVOKE:
                    exec_method = bc_item;
                    break;

                case ACTION_NATIVE:
                    go = handle_native();
                    break;

                case ACTION_NEW:
                    go = handle_new(bc_item);
                    break;

                case ACTION_THROW:
                    go = handle_throw((ref_t *)bc_item.ptr);
                    break;
            }
            if (bc_action == ACTION_INVOKE)
                break;

            if (go == false)
                return false;
        }
    }
    return false;
}


/****************************************************************************
* Supporting function for run().
* Performs preparation procedures for searching the exception handler.
*
* Input:        method  pointer to currently executing method
* Output:       none
* Return:       true    if code execution should continue
*               false   if exception has been generated and currently
*                       running method should return
****************************************************************************/
bool handle_excep(void)
{
    int16 excep, detail;

    fillInStackTrace();
    excep = getExceptionIndex(&detail);
    if (excep == 0 || excep > RUNTIME_EXCEPTIONS)
    {
        return false;
    }

    /* save current run-time exception code */
    vm_excep1 = vm_excep;
    vm_excep = 0;

    return handle_throw(excep_refs[excep-1]);
}


/****************************************************************************
* Supporting function for run().
* Gets native method lookup table index. For system menthods (java/lang)
* index is adjusted by SYS_BASE_INDEX value. Then, appropriate offset is
* calculated either into system or application lookup tables.
* 'C' native must have following prototype:
*              void method(int32 natParams[], int32 *retValue);
*
* Functions expects that the bc_item has been setup by the invocation bytecode.
*
* Input:        none
* Output:       none
* Return:       true    if code execution should continue
*               false   if exception has been generated and currently
*                       running method should return
****************************************************************************/
static bool handle_native(void)
{
    int16 index;
    value_t retval;


    /* check if it is system native method */
    config->switchBank(bc_item.bank);
    if (((method_t *)bc_item.ptr)->blen_idx >= SYS_BASE_INDEX)
    {
        /* run system native method */
        index = (int16)(((method_t *)bc_item.ptr)->blen_idx - SYS_BASE_INDEX);
        vm_excep = (uint8)sys_native_tbl[index]((int32 *)vm_args, (int32 *)&retval);
    }
    else
    {
        /* run user application native method */
        vm_excep = (uint8)config->native_tbl[((method_t *)bc_item.ptr)->blen_idx]((int32 *)vm_args, (int32 *)&retval);
    }
    config->switchBank(vm_curr_bank);

    if (vm_excep != 0)
        return handle_excep();

    if (vm_status & RETVAL_FLAG)
        *vm_sp++ = retval;

    return true;
}


/****************************************************************************
* Supporting function for run().
* Handles returns from methods.
*
* Input:        init_frame  pointer first frame created by run()
* Output:       none
* Return:       true    if code execution should continue
*               false   if there are no more methods on frames stack
*
****************************************************************************/
static int16 handle_return(frame_t *init_frame)
{
    value_t rv;

    /*
    ** Special handling if RUN_SPECIAL_FLAG is set.
    ** run() will return when init_frame equals current one
    */
    if (vm_status & RUN_SPECIAL_FLAG && init_frame == thr_active->curr_frame)
    {
        if (!releaseFrame(&rv))
            thr_active->curr_frame = NULL;
        return RUN_RETURN;
    }

    if (releaseFrame(&rv))
    {
        if (vm_status & RETVAL_FLAG)
            *vm_sp++ = rv;

        return METH_RETURN;
    }

    /* no more frames on the stack -- terminate this thread */
    deleteThread(thr_active);
    if (thr_count > 0)
      return METH_RETURN;

    return RETURN_FAIL;
}


/****************************************************************************
* Supporting function for run().
* Creates new instance of object specified by the supplied class structure.
*
* Functions expects that the bc_item has been setup by the 'new' bytecode.
*
* Input:        class_ptr   structure containing location of a new class object
* Output:       none
* Return:       true    if code execution should continue
*               false   if exception has been generated and currently
*                       running method should return
****************************************************************************/
static bool handle_new(bitem_t class_ptr)
{
    ref_t *ref;

    /* Run <clinit> on the new object if RUN_SPECIAL_FLAG is set */
    if (vm_status & RUN_SPECIAL_FLAG)
    {
        if (!run_clinit(class_ptr))
            return false;
    }

    /* adjust ptr to relative offset as createObject will set it up again */
    class_ptr.ptr = BANK_REL_LOC(class_ptr.ptr);
    ref = createObject(class_ptr);
    if (ref == NULL)
        return handle_excep();

    /* put object's reference onto stack */
    (*vm_sp++).val = PUT_REF(ref);

    return true;
}


/****************************************************************************
* Supporting function for run().
* Searches trough the currently excuting method exception table to see if
* it has appropriate handlers. If not then exception is re-thrown in calling
* method. Process repeats until exception handler is found or there are no
* more calling methods.
*
* Input:        excep   pointer to reference of thrown exception object
* Output:       none
* Return:       true    if exception has been catched and exception handler
*                       code has to be executed
*               false   if exception hasn't been caught and method should
*                       return
****************************************************************************/
static bool handle_throw(ref_t *excep)
{
    uint8 *new_pc;
    value_t rv;

    while (true)
    {
        new_pc = findExcepEntry(excep);
        if (new_pc != NULL)
        {
            /* reset stack pointer */
            vm_sp = (value_t *)((uint8 *)thr_active->curr_frame + FRAME_HDR_SIZE +
                                ((method_t *)thr_active->curr_frame->method.ptr)->locals);

            /* push thrown exception object onto stack */
            (*vm_sp++).val = PUT_REF(excep);

            /* start excep. handler to run */
            vm_pc = new_pc;
            return true;
        }

#if ENABLE_THREADS
        /* release synchronization locks -- if any */
        if (!releaseLocks())
            return false;
#endif
        /* restore previous method's frame so exception could be re-thrown */
        if (releaseFrame(&rv))
        {
            /*
            ** Prev. metod's frame restored - re-throw exception in this method by
            ** pushing exception object onto stack and running bc_athorw
            */
            /*
            (*vm_sp++).val = PUT_REF(excep);
            BC_athrow();
            */
            continue;
        }

        /* restore run-time exception -- if exists */
        if (vm_excep1 != 0)
            vm_excep = vm_excep1;
        else
            vm_excep = INTERR_UnhandledExcep;

        return false;
    }
}


/****************************************************************************
* Creates new java object on the heap.
*
* Input:        class_ptr   ptr. to class structure
* Output:       none
* Return:       pointer to object's reference if successful, othrewise
*               NULL is returned.
****************************************************************************/
ref_t *createObject(bitem_t class_ptr)
{
    ref_t *ref;

    ref = allocHeapMem(inst_size);

    if (ref != NULL)
    {
        config->switchBank(class_ptr.bank);
        ref->class_ptr.bank = class_ptr.bank;
        ref->class_ptr.ptr = BANK_ABS_LOC(class_ptr.ptr);
        ref->ptr->flag |= OF_CLASS;
        config->switchBank(vm_curr_bank);
    }

    return ref;
}


/****************************************************************************
* Runs first method in method table -- which is always <init>(). If strRef is
* not NULL then second initialisation method is run --  <init>(java/lang/String)
* It is guaranteed by the ClassLinker that <init>() is always first.
*
* Input:        ref     pointer to reference of the object for which init
*                       should be run
*               strRef  pointer to string object
* Output:       none
* Return:       true if successful, otherwise false is returned.
****************************************************************************/
bool run_init(ref_t *ref, ref_t *strRef)
{
    value_t param[2];
    method_tbl_t *meth_tbl;
    bitem_t method;

    param[0].val = PUT_REF(ref);
    vm_args_count = 1;
    if (strRef != NULL)
    {
        param[1].val = PUT_REF(strRef);
        vm_args_count++;
    }
    vm_args = param;
    vm_status |= RUN_SPECIAL_FLAG;

    config->switchBank(ref->class_ptr.bank);
    meth_tbl = (method_tbl_t *)BANK_ABS_LOC(((class_t *)ref->class_ptr.ptr)->meth_tbl);
    if (strRef == NULL)
        method.ptr = BANK_ABS_LOC(meth_tbl[0].method);
    else
        method.ptr = BANK_ABS_LOC(meth_tbl[1].method);
    method.bank = ref->class_ptr.bank;
    return run(method);
}


/****************************************************************************
* Creates new java/langString object.
* Calling process should test for possible exceptions.
*
* Input:        cons    ptr. to string constant info. structure
* Output:       none
* Return:       pointer to String's object reference if successful, othrewise
*               NULL is returned.
****************************************************************************/
ref_t *createStringObject(const_t *cons)
{
    ref_t *str_ref, *arr_ref = NULL;
    jchar *dest;
    int8 *src;
    arrhdr_t *arr_hdr;
    uint16 i;
    ptrval size;

    /* for loop allows to re-allocate objects in case GC was run during allocation process */
    for (i=0; i < ALLOC_TRY; i++)
    {
        /* allocate memory for new String object and initialise header */
        str_ref = allocHeapMem(inst_size);
        if (str_ref == NULL)
            return NULL;

        str_ref->class_ptr.bank = APP->string.bank;
        str_ref->class_ptr.ptr = BANK_ABS_LOC(APP->string.ptr);
        str_ref->ptr->flag |= OF_CLASS;

        if (!addToStrTable(CONST_STR_HASH(cons), str_ref))
            return NULL;
        /* re-allocate objects if GC was run and str_ref has been freed */
        if ((vm_status & GC_DONE_FLAG) == 0)
        {
            /* allocate memory for string array object and initialise header */
#if ENABLE_UNICODE
            size = (ptrval)((CONST_LEN(cons->type_len) << 1) + sizeof(arrhdr_t));
#else
            size = (ptrval)(CONST_LEN(cons->type_len) + sizeof(arrhdr_t));
#endif
            if (size < inst_size)
                    size = inst_size;

            arr_ref = allocHeapMem(size);
            if (arr_ref == NULL)
                return NULL;

            arr_ref->ptr->flag |= OF_ARRAY;

            if ((vm_status & GC_DONE_FLAG) == 0)
                break;
        }

        removeFromStrTable(str_ref);
    }
    if (i == ALLOC_TRY)
    {
        vm_excep = vm_excep1;
        return NULL;
    }

    arr_hdr = (arrhdr_t *)arr_ref->ptr;
    arr_hdr->count = (uint16)CONST_LEN(cons->type_len);
    arr_hdr->type = (uint8)T_CHAR;

    /* copy characters */
    src = CONST_STR(cons);
    dest = (jchar *)((uint8 *)arr_hdr + sizeof(arrhdr_t));
    for (i=0; i < (uint16)CONST_LEN(cons->type_len); i++)
        dest[i] = src[i];

    /* initialise String's count, offset and value fields */
    config->switchBank(APP->string.bank);
    setField(str_ref, FIELD_LOC(APP->string_value), PUT_REF(arr_ref));
    setField(str_ref, FIELD_LOC(APP->string_offset), 0);
    setField(str_ref, FIELD_LOC(APP->string_count), (uint32)arr_hdr->count);
    config->switchBank(vm_curr_bank);

    return str_ref;
}


/****************************************************************************
* Sets the instance field to specified value;
*
* Input:        ref     pointer to object's reference
*               field   pointer to field info structure
*               value   new value for the field
* Output:       none
* Return:       none
****************************************************************************/
static void setField(ref_t *ref, field_t *field, uint32 value)
{
    value_t *fields;

    fields = (value_t *)((uint8 *)ref->ptr + sizeof(classhdr_t));
    fields[FLD_INDEX(field->index)].i = value;
}


/****************************************************************************
* Checks if the class S is subclass of class T.
* Checking is done by comparing s and t. If they don't match then
* location of super class S is retrieved and check is performed again.
* This is repeated until match is found or there are no more super classes
* of class S.
*
* Input:        S       pointer to class S
*               T       pointer to class T
* Output:       none
* Return:       true if S is subclass of T, otherwise false is returned
****************************************************************************/
bool isSubclass(bitem_t S, bitem_t T)
{
    bool retval = false;
    while (true)
    {
        if (S.bank == T.bank && S.ptr == T.ptr)
        {
            retval = true;
            break;
        }

        config->switchBank(S.bank);
        if (S.ptr == NULL || ((class_t *)S.ptr)->super.ptr == NULL)
            break;

        S = ((class_t *)S.ptr)->super;
        S.ptr = BANK_ABS_LOC(S.ptr);
    }
    config->switchBank(vm_curr_bank);
    return retval;
}



/****************************************************************************
* Goes trough the exception table of the current method and checks if there
* is an entry with matching values for the PC. If found then it checks if
* thrown exception object is subclass of exception type specified by the
* table entry.
*
* Input:        excep_ref   reference to thrown exception object
* Output:       none
* Return:       new PC if mathing table entry found, otherwise NULL is
*               returned
****************************************************************************/
static uint8 *findExcepEntry(ref_t *excep_ref)
{
    uint16 i;
    bitem_t method;
    excep_tbl_t *excep_tbl;
    uint16 rel_pc;

    method = thr_active->curr_frame->method;
    excep_tbl = (excep_tbl_t *)(method.ptr + ((method_t *)method.ptr)->blen_idx + sizeof(method_t));

    rel_pc = (uint16)(vm_pc - 1 - (method.ptr + sizeof(method_t)));
    for (i=0; i < excep_tbl->entries; i++)
    {
        /* Check if current PC is within the range: start <= rel_pc < end */
        if (rel_pc >= excep_tbl->excep[i].start && rel_pc < excep_tbl->excep[i].end)
        {
            /*
            ** Check if it's finally clause (-1) or if thrown exception is class or
            ** subclass of objet specified in table entry.
            */
            if (excep_tbl->excep[i].excep_idx == (uint16)0xFFFF ||
                isSubclass(excep_ref->class_ptr, getLocation(excep_tbl->excep[i].excep_idx)))
            {
                return (uint8 *)(method.ptr + sizeof(method_t) + excep_tbl->excep[i].handler);
            }
        }
    }

    return NULL;
}


/****************************************************************************
* Expand signed uint8 value into java integer value.
*
* Input:        bval    signed uint8 value
* Output:       none
* Return:       sign expanded uint8 value
****************************************************************************/
int32 expandByte(int8 bval)
{
    if (bval & 0x80)
        return (0xFFFFFF00L | bval);

    return bval;
}


/****************************************************************************
* Expand signed short value (16 bit) into java integer value.
*
* Input:        sval    signed short value
* Output:       none
* Return:       sign expanded short value
****************************************************************************/
int32 expandShort(int16 sval)
{
    if (sval & 0x8000)
        return (0xFFFF0000L | sval);

    return sval;
}


/****************************************************************************
* Adds new entry to vm_strings_start. if table if full EXCEP_StrTableFull
* is thrown.
*
* Input:        hash    string's hashcode
*               string  reference to string object
* Output:       none
* Return:       true if successfull, otherwise false is returned
****************************************************************************/
static bool addToStrTable(uint16 hash, ref_t *string)
{
    uint16 i;

    vm_status &= ~GC_DONE_FLAG;

    while (true)
    {
        /* find first empty slot in the table */
        for (i=0; i < config->strings; i++)
        {
            if (strings[i].ref == NULL)
                break;
        }
        if (i < config->strings)
            break;

#if ENABLE_GC
        /* if not found and GC has been run -- generate exception */
        if (vm_status & GC_DONE_FLAG)
        {
            vm_excep = MEMERR_StrTableFull;
            return false;
        }

        /* run GC at this point to see if some table entries are freed */
        if (!walkAll())
            return false;
#else
        vm_excep = MEMERR_StrTableFull;
        return false;
#endif
    }

    strings[i].ref = string;
    strings[i].hash = hash;

    return true;
}


/****************************************************************************
* Removes entry containg matching object's reference.
*
* Input:        string  reference to string object
* Output:       none
* Return:       none
****************************************************************************/
void removeFromStrTable(ref_t *string)
{
    uint16 i;

    for (i=0; i < config->strings; i++)
    {
        if (strings[i].ref == string)
        {
            strings[i].ref = NULL;
            strings[i].hash = (uint16)0;
            return;
        }
    }
}


/****************************************************************************
* Tries to find matching entry in vm_strings_start. Searching will
* stop when entry with matching hashcode is found and refernce
* to corresponding string object is returned.
*
* Input:        hash    string's hashcode
* Output:       none
* Return:       reference to coresponding string object if successfull,
*               NULL if no matching entry has been found.
****************************************************************************/
ref_t *findInStrTable(uint16 hash)
{
    uint16 i;

    for (i=0; i < config->strings; i++)
    {
        if (strings[i].hash == hash)
            return strings[i].ref;
    }
    return NULL;
}


/****************************************************************************
* Resolves the reference to the specified String object and
* returns the pointer to array of chars starting at offset
* specified by String.offset field.
*
* Input:        string  pointer to reference of the String object
* Output:       none
* Return:       pointer to int8 array of this String object
****************************************************************************/
static jchar *getStringArray(int32 param)
{
    jchar *retval = NULL;
    ref_t *arrobj, *string;
    arrhdr_t *arrhdr;
    value_t *fields;
    uint32 offs;

    string = GET_REF(param);
    if (string != NULL)
    {
        /* get pointer to string object instance data */
        fields = (value_t *)((uint8 *)string->ptr + sizeof(classhdr_t));
        config->switchBank(APP->string.bank);
        arrobj = GET_REF(fields[FLD_INDEX(FIELD_LOC(APP->string_value)->index)].i);
        if (arrobj != NULL)
        {
            offs = fields[FLD_INDEX(FIELD_LOC(APP->string_offset)->index)].i;
#if ENABLE_UNICODE
            offs <<= 1;
#endif
            arrhdr = (arrhdr_t *)arrobj->ptr;
            retval = (jchar *)((uint8 *)arrhdr + sizeof(arrhdr_t) + offs);
        }
        config->switchBank(vm_curr_bank);
    }
    return retval;
}

/****************************************************************************
* Resolves the reference to the specified String object and
* returns the number of characters contained in this String
* object.
*
* Input:        string  pointer to reference of the String object
* Output:       none
* Return:       length of the string
****************************************************************************/
static jint getStringCount(int32 param)
{
    ref_t *string;
    value_t *fields;
    jint rv;

    string = GET_REF(param);
    if (string != NULL)
    {
        fields = (value_t *)((uint8 *)string->ptr + sizeof(classhdr_t));
        config->switchBank(APP->string.bank);
        rv = fields[FLD_INDEX(FIELD_LOC(APP->string_count)->index)].i;
        config->switchBank(vm_curr_bank);
        return rv;
    }
    return 0;
}

/****************************************************************************
* Resolves the reference to the specified String object and
* returns the pointer to array of chars starting at offset
* specified by String.offset field.
*
* Input:        string  pointer to reference of the String object
* Output:       none
* Return:       pointer to int8 array of this String object
****************************************************************************/
static jint getArraySize(int32 param)
{
    ref_t *ref;

    ref = GET_REF(param);
    if (ref != NULL && ref->ptr != NULL)
        return ((arrhdr_t *)ref->ptr)->count;

    return 0;
}

/****************************************************************************
* Resolves the reference to the specified String object and
* returns the pointer to array of chars starting at offset
* specified by String.offset field.
*
* Input:        string  pointer to reference of the String object
* Output:       none
* Return:       pointer to int8 array of this String object
****************************************************************************/
static void *getArray(int32 param)
{
    ref_t *ref;

    ref = GET_REF(param);
    if (ref != NULL && ref->ptr != NULL)
        return (uint8 *)ref->ptr + sizeof(arrhdr_t);

    return NULL;
}

/****************************************************************************
* This routine checks if the application at vm_app_start has the correct
* magic number, ClassLinker version and calculates LRC over specified area
* of memory.
*
* Input:        none
* Output:       none
* Return:       true if successful, otherwise false is returned
****************************************************************************/
static bool verifyApp(void)
{
    uint16 i;
    uint8 *ptr;
    uint8 sum = 0;

    if (APP->magic == MAGIC)
    {
        if (APP->version == SW_VER)
        {
            ptr = APP->csum_start + (ptrval)config->app_start;
            for (i=0; i < APP->csum_len; i++)
            {
                sum ^= ptr[i];
            }
            if (sum == (uint8)APP->csum_lrc)
                return true;
        }
    }
    return false;
}

/*
* Returns current VM system time.
*/
static uint32 getSysTime(void)
{
    return vm_time_ms;
}

/*
* Updates specified event counter and forces event thread to run
* immediatelly.
*/
static void updateEventCounter(uint16 index)
{
    config->ev_counters[index]++;
    thr_switch++;
}

/*
* Runs the specified function specified in config.
*/
static void usrFunc(void (*f)(uint16, char *), uint16 code)
{
#if ENABLE_TEXT_NOTIFY
    uint16 subcode;
#endif
    if (f != NULL)
    {
#if ENABLE_TEXT_NOTIFY
        subcode = (uint16)(code & 0xFF);
        if (subcode > NOTIFY_VM)
            f(code, (char *)notify_msg[subcode-NOTIFY_VM-1]);
        else if (subcode > MEMERR)
            f(code, (char *)mem_excep_msg[subcode-MEMERR-1]);
        else if (subcode > INTERR)
            f(code, (char *)int_excep_msg[subcode-INTERR-1]);
        else
            f(code, (char *)excep_msg[(code >> 8)-1]);
#else
        f(code, NULL);
#endif
    }
}



